package prefuse.data.expression;

import java.text.DecimalFormat;
import java.text.NumberFormat;

import prefuse.data.Edge;
import prefuse.data.Node;
import prefuse.data.Schema;
import prefuse.data.Tuple;
import prefuse.util.ColorLib;
import prefuse.util.MathLib;
import prefuse.util.StringLib;
import prefuse.util.collections.CopyOnWriteArrayList;

/**
 * Abstract base class for FunctionExpression implementations. Provides
 * parameter handling support.
 * 
 * @author <a href="http://jheer.org">jeffrey heer</a>
 */
public abstract class FunctionExpression extends AbstractExpression implements Function {
    protected CopyOnWriteArrayList m_params;
    protected final int m_pcount;

    /**
     * Protected constructor.
     * @param parameterCount the max parameter count
     */
    protected FunctionExpression(int parameterCount) {
        m_pcount = parameterCount;
    }

    /**
     * @see prefuse.data.expression.Function#getName()
     */
    public abstract String getName();

    /**
     * @see prefuse.data.expression.Function#addParameter(prefuse.data.expression.Expression)
     */
    public void addParameter(Expression e) {
        int pc = getParameterCount();
        if (pc != VARARGS && paramCount() + 1 > pc) {
            throw new IllegalStateException("This function takes only " + pc + " parameters.");
        }
        if (m_params == null)
            m_params = new CopyOnWriteArrayList();
        m_params.add(e);
    }

    /**
     * An internal-only method that returns the current number of
     * parameters collected.
     */
    protected int paramCount() {
        return m_params == null ? 0 : m_params.size();
    }

    /**
     * Return the parameter expression at the given index.
     * @param idx the parameter index
     * @return the parameter value Expression at the given index
     */
    protected final Expression param(int idx) {
        return (Expression) m_params.get(idx);
    }

    /**
     * @see prefuse.data.expression.Function#getParameterCount()
     */
    public int getParameterCount() {
        return m_pcount;
    }

    /**
     * Throw an exception when needed parameters are missing.
     */
    protected void missingParams() {
        throw new IllegalStateException("Function is missing parameters: " + getName());
    }

    // ------------------------------------------------------------------------

    /**
     * @see prefuse.data.expression.Expression#visit(prefuse.data.expression.ExpressionVisitor)
     */
    public void visit(ExpressionVisitor v) {
        v.visitExpression(this);
        if (paramCount() > 0) {
            Object[] params = m_params.getArray();
            for (int i = 0; i < params.length; ++i) {
                v.down();
                ((Expression) params[i]).visit(v);
                v.up();
            }
        }
    }

    /**
     * @see prefuse.data.expression.AbstractExpression#addChildListeners()
     */
    protected void addChildListeners() {
        if (paramCount() > 0) {
            Object[] params = m_params.getArray();
            for (int i = 0; i < params.length; ++i)
                ((Expression) params[i]).addExpressionListener(this);
        }
    }

    /**
     * @see prefuse.data.expression.AbstractExpression#removeChildListeners()
     */
    protected void removeChildListeners() {
        if (paramCount() > 0) {
            Object[] params = m_params.getArray();
            for (int i = 0; i < params.length; ++i)
                ((Expression) params[i]).removeExpressionListener(this);
        }
    }

    /**
     * @see java.lang.Object#toString()
     */
    public String toString() {
        StringBuffer sbuf = new StringBuffer();
        sbuf.append(getName()).append('(');
        for (int i = 0; i < paramCount(); ++i) {
            if (i > 0)
                sbuf.append(", ");
            sbuf.append(param(i).toString());
        }
        sbuf.append(')');
        return sbuf.toString();
    }

} // end of class FunctionExpression

/**
 * Default Function Implementations
 */

// ----------------------------------------------------------------------------
// Mathematical Functions

abstract class DoubleFunction extends FunctionExpression {
    protected DoubleFunction(int parameterCount) {
        super(parameterCount);
    }

    public Class getType(Schema s) {
        return double.class;
    }

    public Object get(Tuple t) {
        return new Double(getDouble(t));
    }

    public int getInt(Tuple t) {
        return (int) getDouble(t);
    }

    public long getLong(Tuple t) {
        return (long) getDouble(t);
    }

    public float getFloat(Tuple t) {
        return (float) getDouble(t);
    }
}

abstract class IntFunction extends FunctionExpression {
    protected IntFunction(int parameterCount) {
        super(parameterCount);
    }

    public Class getType(Schema s) {
        return int.class;
    }

    public Object get(Tuple t) {
        return new Integer(getInt(t));
    }

    public long getLong(Tuple t) {
        return (long) getInt(t);
    }

    public float getFloat(Tuple t) {
        return (float) getInt(t);
    }

    public double getDouble(Tuple t) {
        return (double) getInt(t);
    }
}

abstract class BooleanFunction extends FunctionExpression implements Predicate {
    protected BooleanFunction(int parameterCount) {
        super(parameterCount);
    }

    public Class getType(Schema s) {
        return boolean.class;
    }

    public Object get(Tuple t) {
        return getBoolean(t) ? Boolean.TRUE : Boolean.FALSE;
    }
}

//ROW()
class RowFunction extends IntFunction {
    public RowFunction() {
        super(0);
    }

    public String getName() {
        return "ROW";
    }

    public int getInt(Tuple t) {
        return t.getRow();
    }
}

//ISNODE()
class IsNodeFunction extends BooleanFunction {
    public IsNodeFunction() {
        super(0);
    }

    public String getName() {
        return "ISNODE";
    }

    public boolean getBoolean(Tuple t) {
        return (t instanceof Node);
    }
}

//ISEDGE()
class IsEdgeFunction extends BooleanFunction {
    public IsEdgeFunction() {
        super(0);
    }

    public String getName() {
        return "ISEDGE";
    }

    public boolean getBoolean(Tuple t) {
        return (t instanceof Edge);
    }
}

//DEGREE()
class DegreeFunction extends IntFunction {
    public DegreeFunction() {
        super(0);
    }

    public String getName() {
        return "DEGREE";
    }

    public int getInt(Tuple t) {
        return (t instanceof Node ? ((Node) t).getDegree() : 0);
    }
}

//INDEGREE()
class InDegreeFunction extends IntFunction {
    public InDegreeFunction() {
        super(0);
    }

    public String getName() {
        return "INDEGREE";
    }

    public int getInt(Tuple t) {
        return (t instanceof Node ? ((Node) t).getInDegree() : 0);
    }
}

//OUTDEGREE()
class OutDegreeFunction extends IntFunction {
    public OutDegreeFunction() {
        super(0);
    }

    public String getName() {
        return "OUTDEGREE";
    }

    public int getInt(Tuple t) {
        return (t instanceof Node ? ((Node) t).getOutDegree() : 0);
    }
}

//CHILDCOUNT()
class ChildCountFunction extends IntFunction {
    public ChildCountFunction() {
        super(0);
    }

    public String getName() {
        return "CHILDCOUNT";
    }

    public int getInt(Tuple t) {
        return (t instanceof Node ? ((Node) t).getChildCount() : 0);
    }
}

//TREEDEPTH()
class TreeDepthFunction extends IntFunction {
    public TreeDepthFunction() {
        super(0);
    }

    public String getName() {
        return "TREEDEPTH";
    }

    public int getInt(Tuple t) {
        return (t instanceof Node ? ((Node) t).getDepth() : 0);
    }
}

//ABS(X)
class AbsFunction extends DoubleFunction {
    public AbsFunction() {
        super(1);
    }

    public String getName() {
        return "ABS";
    }

    public double getDouble(Tuple t) {
        if (paramCount() == 1) {
            return Math.abs(param(0).getDouble(t));
        } else {
            missingParams();
            return Double.NaN;
        }
    }
}

//ACOS(X)
class AcosFunction extends DoubleFunction {
    public AcosFunction() {
        super(1);
    }

    public String getName() {
        return "ACOS";
    }

    public double getDouble(Tuple t) {
        if (paramCount() == 1) {
            return Math.acos(param(0).getDouble(t));
        } else {
            missingParams();
            return Double.NaN;
        }
    }
}

//ASIN(X)
class AsinFunction extends DoubleFunction {
    public AsinFunction() {
        super(1);
    }

    public String getName() {
        return "ASIN";
    }

    public double getDouble(Tuple t) {
        if (paramCount() == 1) {
            return Math.asin(param(0).getDouble(t));
        } else {
            missingParams();
            return Double.NaN;
        }
    }
}

//ATAN(X)
class AtanFunction extends DoubleFunction {
    public AtanFunction() {
        super(1);
    }

    public String getName() {
        return "ATAN";
    }

    public double getDouble(Tuple t) {
        if (paramCount() == 1) {
            return Math.atan(param(0).getDouble(t));
        } else {
            missingParams();
            return Double.NaN;
        }
    }
}

//ATAN2(Y,X)
class Atan2Function extends DoubleFunction {
    public Atan2Function() {
        super(2);
    }

    public String getName() {
        return "ATAN2";
    }

    public double getDouble(Tuple t) {
        if (paramCount() == 2) {
            return Math.atan2(param(0).getDouble(t), param(1).getDouble(t));
        } else {
            missingParams();
            return Double.NaN;
        }
    }
}

//CEILING(X), CEIL(X)
class CeilFunction extends DoubleFunction {
    public CeilFunction() {
        super(1);
    }

    public String getName() {
        return "CEIL";
    }

    public double getDouble(Tuple t) {
        if (paramCount() == 1) {
            return Math.ceil(param(0).getDouble(t));
        } else {
            missingParams();
            return Double.NaN;
        }
    }

    public int getInt(Tuple t) {
        return (int) getDouble(t);
    }
}

//COS(X)
class CosFunction extends DoubleFunction {
    public CosFunction() {
        super(1);
    }

    public String getName() {
        return "COS";
    }

    public double getDouble(Tuple t) {
        if (paramCount() == 1) {
            return Math.cos(param(0).getDouble(t));
        } else {
            missingParams();
            return Double.NaN;
        }
    }
}

//COT(X)
class CotFunction extends DoubleFunction {
    public CotFunction() {
        super(1);
    }

    public String getName() {
        return "COT";
    }

    public double getDouble(Tuple t) {
        if (paramCount() == 1) {
            return 1 / Math.tan(param(0).getDouble(t));
        } else {
            missingParams();
            return Double.NaN;
        }
    }
}

//DEGREES(X)
class DegreesFunction extends DoubleFunction {
    public DegreesFunction() {
        super(1);
    }

    public String getName() {
        return "DEGREES";
    }

    public double getDouble(Tuple t) {
        if (paramCount() == 1) {
            return Math.toDegrees(param(0).getDouble(t));
        } else {
            missingParams();
            return Double.NaN;
        }
    }
}

//E()
class EFunction extends DoubleFunction {
    public EFunction() {
        super(0);
    }

    public String getName() {
        return "E";
    }

    public double getDouble(Tuple t) {
        return Math.E;
    }
}

//EXP(X)
class ExpFunction extends DoubleFunction {
    public ExpFunction() {
        super(1);
    }

    public String getName() {
        return "EXP";
    }

    public double getDouble(Tuple t) {
        if (paramCount() == 1) {
            return Math.exp(param(0).getDouble(t));
        } else {
            missingParams();
            return Double.NaN;
        }
    }
}

//FLOOR(X)
class FloorFunction extends DoubleFunction {
    public FloorFunction() {
        super(1);
    }

    public String getName() {
        return "FLOOR";
    }

    public double getDouble(Tuple t) {
        if (paramCount() == 1) {
            return Math.floor(param(0).getDouble(t));
        } else {
            missingParams();
            return Double.NaN;
        }
    }

    public int getInt(Tuple t) {
        return (int) getDouble(t);
    }
}

//LOG(X), LOG(B,X)
class LogFunction extends DoubleFunction {
    public LogFunction() {
        super(2);
    }

    public String getName() {
        return "LOG";
    }

    public double getDouble(Tuple t) {
        int pc = paramCount();
        if (pc == 2) {
            double b = param(0).getDouble(t);
            double x = param(1).getDouble(t);
            return Math.log(x) / Math.log(b);
        } else if (pc == 1) {
            return Math.log(param(0).getDouble(t));
        } else {
            missingParams();
            return Double.NaN;
        }
    }
}

//LOG2(X)
class Log2Function extends DoubleFunction {
    public Log2Function() {
        super(1);
    }

    public String getName() {
        return "LOG2";
    }

    public double getDouble(Tuple t) {
        if (paramCount() == 1) {
            return MathLib.log2(param(0).getDouble(t));
        } else {
            missingParams();
            return Double.NaN;
        }
    }
}

//LOG10(X)
class Log10Function extends DoubleFunction {
    public Log10Function() {
        super(1);
    }

    public String getName() {
        return "LOG10";
    }

    public double getDouble(Tuple t) {
        if (paramCount() == 1) {
            return MathLib.log10(param(0).getDouble(t));
        } else {
            missingParams();
            return Double.NaN;
        }
    }
}

//SAFELOG10(X)
class SafeLog10Function extends DoubleFunction {
    public SafeLog10Function() {
        super(2);
    }

    public String getName() {
        return "SAFELOG10";
    }

    public double getDouble(Tuple t) {
        int pc = paramCount();
        if (pc == 1) {
            double x = param(0).getDouble(t);
            return MathLib.safeLog10(x);
        } else {
            missingParams();
            return Double.NaN;
        }
    }
}

//MAX(X1,X2,...)
class MaxFunction extends DoubleFunction {
    public MaxFunction() {
        super(Function.VARARGS);
    }

    public String getName() {
        return "MAX";
    }

    public double getDouble(Tuple t) {
        double x, v = param(0).getDouble(t);
        for (int i = 1; i < paramCount(); ++i) {
            x = param(i).getDouble(t);
            if (x > v)
                v = x;
        }
        return v;
    }
}

//MIN(X1,X2,...)
class MinFunction extends DoubleFunction {
    public MinFunction() {
        super(Function.VARARGS);
    }

    public String getName() {
        return "MIN";
    }

    public double getDouble(Tuple t) {
        double x, v = param(0).getDouble(t);
        for (int i = 1; i < paramCount(); ++i) {
            x = param(i).getDouble(t);
            if (x < v)
                v = x;
        }
        return v;
    }
}

//MOD(N,M)
class ModFunction extends DoubleFunction {
    public ModFunction() {
        super(2);
    }

    public String getName() {
        return "MOD";
    }

    public double getDouble(Tuple t) {
        if (paramCount() == 2) {
            double x = param(0).getDouble(t);
            double y = param(1).getDouble(t);
            return x % y;
        } else {
            missingParams();
            return Double.NaN;
        }
    }
}

//PI()
class PiFunction extends DoubleFunction {
    public PiFunction() {
        super(0);
    }

    public String getName() {
        return "PI";
    }

    public double getDouble(Tuple t) {
        return Math.PI;
    }
}

//POW(X,Y), POWER(X,Y)
class PowFunction extends DoubleFunction {
    public PowFunction() {
        super(1);
    }

    public String getName() {
        return "POW";
    }

    public double getDouble(Tuple t) {
        if (paramCount() == 2) {
            return Math.pow(param(0).getDouble(t), param(1).getDouble(t));
        } else {
            missingParams();
            return Double.NaN;
        }
    }
}

//RADIANS(X)
class RadiansFunction extends DoubleFunction {
    public RadiansFunction() {
        super(1);
    }

    public String getName() {
        return "RADIANS";
    }

    public double getDouble(Tuple t) {
        if (paramCount() == 1) {
            return Math.toRadians(param(0).getDouble(t));
        } else {
            missingParams();
            return Double.NaN;
        }
    }
}

//RAND(), RAND(N)
class RandFunction extends DoubleFunction {
    public RandFunction() {
        super(0);
    }

    public String getName() {
        return "RAND";
    }

    public double getDouble(Tuple t) {
        return Math.random();
    }
}

//ROUND(X) // --> ROUND(X,D) TODO
class RoundFunction extends DoubleFunction {
    public RoundFunction() {
        super(1);
    }

    public String getName() {
        return "ROUND";
    }

    public double getDouble(Tuple t) {
        if (paramCount() == 1) {
            return Math.round(param(0).getDouble(t));
        } else {
            missingParams();
            return Double.NaN;
        }
    }

    public int getInt(Tuple t) {
        return (int) getDouble(t);
    }
}

//SIGN(X)
class SignFunction extends DoubleFunction {
    public SignFunction() {
        super(1);
    }

    public String getName() {
        return "SIGN";
    }

    public Class getType(Schema s) {
        return int.class;
    }

    public double getDouble(Tuple t) {
        return getInt(t);
    }

    public int getInt(Tuple t) {
        if (paramCount() == 1) {
            double d = param(0).getDouble(t);
            return d < 0 ? -1 : d == 0 ? 0 : 1;
        } else {
            missingParams();
            return Integer.MIN_VALUE;
        }
    }
}

//SIN(X)
class SinFunction extends DoubleFunction {
    public SinFunction() {
        super(1);
    }

    public String getName() {
        return "SIN";
    }

    public double getDouble(Tuple t) {
        if (paramCount() == 1) {
            return Math.sin(param(0).getDouble(t));
        } else {
            missingParams();
            return Double.NaN;
        }
    }
}

//SQRT(X)
class SqrtFunction extends DoubleFunction {
    public SqrtFunction() {
        super(1);
    }

    public String getName() {
        return "SQRT";
    }

    public double getDouble(Tuple t) {
        if (paramCount() == 1) {
            return Math.sqrt(param(0).getDouble(t));
        } else {
            missingParams();
            return Double.NaN;
        }
    }
}

//SUM(X1,X2,...)
class SumFunction extends DoubleFunction {
    public SumFunction() {
        super(Function.VARARGS);
    }

    public String getName() {
        return "SUM";
    }

    public double getDouble(Tuple t) {
        double v = param(0).getDouble(t);
        for (int i = 1; i < paramCount(); ++i)
            v += param(i).getDouble(t);
        return v;
    }
}

//SAFESQRT(X)
class SafeSqrtFunction extends DoubleFunction {
    public SafeSqrtFunction() {
        super(2);
    }

    public String getName() {
        return "SAFESQRT";
    }

    public double getDouble(Tuple t) {
        int pc = paramCount();
        if (pc == 1) {
            double x = param(0).getDouble(t);
            return MathLib.safeSqrt(x);
        } else {
            missingParams();
            return Double.NaN;
        }
    }
}

//TAN(X)
class TanFunction extends DoubleFunction {
    public TanFunction() {
        super(1);
    }

    public String getName() {
        return "TAN";
    }

    public double getDouble(Tuple t) {
        if (paramCount() == 1) {
            return Math.tan(param(0).getDouble(t));
        } else {
            missingParams();
            return Double.NaN;
        }
    }
}

//TRUNCATE(X,D)
// TODO

//----------------------------------------------------------------------------
// String Functions

abstract class StringFunction extends FunctionExpression {
    protected StringFunction(int parameterCount) {
        super(parameterCount);
    }

    public Class getType(Schema s) {
        return String.class;
    }

    protected StringBuffer getBuffer() {
        return new StringBuffer();
    }
}

//CAP(str1)
class CapFunction extends StringFunction {
    public CapFunction() {
        super(1);
    }

    public String getName() {
        return "CAP";
    }

    public Object get(Tuple t) {
        String str = param(0).get(t).toString();
        return StringLib.capitalizeFirstOnly(str);
    }
}

//CONCAT(str1,str2,...)
class ConcatFunction extends StringFunction {
    public ConcatFunction() {
        super(Function.VARARGS);
    }

    public String getName() {
        return "CONCAT";
    }

    public Object get(Tuple t) {
        StringBuffer sbuf = getBuffer();
        for (int i = 0; i < paramCount(); ++i) {
            Object o = param(i).get(t);
            if (o != null)
                sbuf.append(o.toString());
        }
        return sbuf.toString();
    }
}

//CONCAT_WS(separator,str1,str2,...)
class ConcatWsFunction extends StringFunction {
    public ConcatWsFunction() {
        super(Function.VARARGS);
    }

    public String getName() {
        return "CONCAT_WS";
    }

    public Object get(Tuple t) {
        StringBuffer sbuf = getBuffer();
        String sep = param(0).get(t).toString();
        for (int i = 1; i < paramCount(); ++i) {
            sbuf.append(param(i).get(t).toString());
            sbuf.append(sep);
        }
        return sbuf.toString();
    }
}

//FORMAT(X,D)
class FormatFunction extends StringFunction {
    public FormatFunction() {
        super(2);
    }

    public String getName() {
        return "FORMAT";
    }

    public Object get(Tuple t) {
        double x = param(0).getDouble(t);
        int d = param(1).getInt(t);
        DecimalFormat df = (DecimalFormat) NumberFormat.getInstance();
        df.setMinimumFractionDigits(d);
        df.setMaximumFractionDigits(d);
        return df.format(x);
    }
}

//INSERT(str,pos,len,newstr)
class InsertFunction extends StringFunction {
    public InsertFunction() {
        super(4);
    }

    public String getName() {
        return "INSERT";
    }

    public Object get(Tuple t) {
        String str = param(0).get(t).toString();
        int strlen = str.length();
        int pos = param(1).getInt(t);
        int len = pos + param(2).getInt(t);
        String newstr = param(1).get(t).toString();
        if (pos < 0 || pos > strlen)
            return str;
        if (len < 0 || len > strlen)
            return str.substring(0, pos) + newstr;
        else
            return str.substring(0, pos) + newstr + str.substring(len);
    }
}

//INSTR(str,substr)
//LEFT(str,len)
class LeftFunction extends StringFunction {
    public LeftFunction() {
        super(2);
    }

    public String getName() {
        return "LEFT";
    }

    public Object get(Tuple t) {
        String src = param(0).get(t).toString();
        int len = param(1).getInt(t);
        return src.substring(0, len);
    }

}

//LENGTH(str)
class LengthFunction extends IntFunction {
    public LengthFunction() {
        super(1);
    }

    public String getName() {
        return "LENGTH";
    }

    public int getInt(Tuple t) {
        return param(0).get(t).toString().length();
    }
}

//LOCATE(substr,str), LOCATE(substr,str,pos)
//LOWER(str) | LCASE(str)
class LowerFunction extends StringFunction {
    public LowerFunction() {
        super(1);
    }

    public String getName() {
        return "LOWER";
    }

    public Object get(Tuple t) {
        return param(0).get(t).toString().toLowerCase();
    }
}

//LPAD(str,len,padstr)
class LPadFunction extends StringFunction {
    public LPadFunction() {
        super(3);
    }

    public String getName() {
        return "LPAD";
    }

    public Object get(Tuple t) {
        String str = param(0).get(t).toString();
        int len = param(1).getInt(t);
        String pad = param(2).get(t).toString();
        int strlen = str.length();
        if (strlen > len) {
            return str.substring(0, len);
        } else if (strlen == len) {
            return str;
        } else {
            StringBuffer sbuf = getBuffer();
            int padlen = pad.length();
            int diff = len - strlen;
            for (int i = 0; i < diff; i += padlen)
                sbuf.append(pad);
            if (sbuf.length() > diff)
                sbuf.delete(diff, sbuf.length());
            sbuf.append(str);
            return sbuf.toString();
        }
    }
}

//LTRIM(str)
//MID(str,pos,len) -- same as substring
//POSITION(substr, str)
class PositionFunction extends IntFunction {
    public PositionFunction() {
        super(2);
    }

    public String getName() {
        return "POSITION";
    }

    public int getInt(Tuple t) {
        String substr = param(0).get(t).toString();
        String src = param(1).get(t).toString();
        return src.indexOf(substr);
    }
}

//QUOTE(str)
//REPEAT(str,count)
class RepeatFunction extends StringFunction {
    public RepeatFunction() {
        super(2);
    }

    public String getName() {
        return "REPEAT";
    }

    public Object get(Tuple t) {
        String src = param(0).get(t).toString();
        int count = param(1).getInt(t);
        StringBuffer sbuf = new StringBuffer();
        for (int i = 0; i < count; ++i) {
            sbuf.append(src);
        }
        return sbuf.toString();
    }
}

//REPLACE(str,from_str,to_str)
class ReplaceFunction extends StringFunction {
    public ReplaceFunction() {
        super(3);
    }

    public String getName() {
        return "REPLACE";
    }

    public Object get(Tuple t) {
        String src = param(0).get(t).toString();
        String from = param(1).get(t).toString();
        String to = param(2).get(t).toString();
        return src.replaceAll(from, to);
    }

}

//REVERSE(str)
class ReverseFunction extends StringFunction {
    public ReverseFunction() {
        super(1);
    }

    public String getName() {
        return "REVERSE";
    }

    public Object get(Tuple t) {
        String str = param(0).get(t).toString();
        StringBuffer sbuf = getBuffer();
        for (int i = str.length() - 1; --i >= 0;) {
            sbuf.append(str.charAt(i));
        }
        return sbuf.toString();
    }
}

//RIGHT(str,len)
class RightFunction extends StringFunction {
    public RightFunction() {
        super(2);
    }

    public String getName() {
        return "RIGHT";
    }

    public Object get(Tuple t) {
        String src = param(0).get(t).toString();
        int len = param(1).getInt(t);
        return src.substring(src.length() - len);
    }

}

//RPAD(str,len,padstr)
class RPadFunction extends StringFunction {
    public RPadFunction() {
        super(3);
    }

    public String getName() {
        return "RPAD";
    }

    public Object get(Tuple t) {
        String str = param(0).get(t).toString();
        int len = param(1).getInt(t);
        String pad = param(2).get(t).toString();
        int strlen = str.length();
        if (strlen > len) {
            return str.substring(0, len);
        } else if (strlen == len) {
            return str;
        } else {
            StringBuffer sbuf = getBuffer();
            sbuf.append(str);
            int padlen = pad.length();
            int diff = len - strlen;
            for (int i = 0; i < diff; i += padlen)
                sbuf.append(pad);
            if (sbuf.length() > len)
                sbuf.delete(len, sbuf.length());
            return sbuf.toString();
        }
    }
}

//RTRIM(str)
//// SOUNDEX(str)
//SPACE(N)
class SpaceFunction extends StringFunction {
    public SpaceFunction() {
        super(1);
    }

    public String getName() {
        return "SPACE";
    }

    public Object get(Tuple t) {
        int n = param(0).getInt(t);
        StringBuffer sbuf = new StringBuffer();
        for (int i = 0; i < n; ++i)
            sbuf.append(' ');
        return sbuf.toString();
    }

}

//SUBSTRING(str,pos), SUBSTRING(str,pos,len)
class SubstringFunction extends StringFunction {
    public SubstringFunction() {
        super(3);
    }

    public String getName() {
        return "SUBSTRING";
    }

    public Object get(Tuple t) {
        String src = param(0).get(t).toString();
        int pos = param(1).getInt(t);
        if (paramCount() == 3) {
            int len = param(2).getInt(t);
            return src.substring(pos, pos + len);
        } else {
            return src.substring(pos);
        }
    }
}

//SUBSTRING_INDEX(str,delim,count)
//TRIM([{BOTH | LEADING | TRAILING} [remstr] FROM] str), TRIM(remstr FROM] str)
//UPPER(str) | UCASE(str)
class UpperFunction extends StringFunction {
    public UpperFunction() {
        super(1);
    }

    public String getName() {
        return "UPPER";
    }

    public Object get(Tuple t) {
        return param(0).get(t).toString().toUpperCase();
    }
}

// ----------------------------------------------------------------------------

//RGB(r,g,b)
class RGBFunction extends IntFunction {
    public RGBFunction() {
        super(3);
    }

    public String getName() {
        return "RGB";
    }

    public int getInt(Tuple t) {
        int r = param(0).getInt(t);
        int g = param(1).getInt(t);
        int b = param(2).getInt(t);
        return ColorLib.rgb(r, g, b);
    }
}

//HEX(hex)
class HexFunction extends IntFunction {
    public HexFunction() {
        super(1);
    }

    public String getName() {
        return "RGB";
    }

    public int getInt(Tuple t) {
        String hex = (String) param(0).get(t);
        return ColorLib.hex(hex);
    }
}

//RGBA(r,g,b,a)
class RGBAFunction extends IntFunction {
    public RGBAFunction() {
        super(4);
    }

    public String getName() {
        return "RGBA";
    }

    public int getInt(Tuple t) {
        int r = param(0).getInt(t);
        int g = param(1).getInt(t);
        int b = param(2).getInt(t);
        int a = param(3).getInt(t);
        return ColorLib.rgba(r, g, b, a);
    }
}

//GRAY(v) | GRAY(v, a)
class GrayFunction extends IntFunction {
    public GrayFunction() {
        super(2);
    }

    public String getName() {
        return "GRAY";
    }

    public int getInt(Tuple t) {
        int g = param(0).getInt(t);
        if (paramCount() == 2) {
            int a = param(1).getInt(t);
            return ColorLib.gray(g, a);
        } else {
            return ColorLib.gray(g);
        }
    }
}

//HSB(h,s,b)
class HSBFunction extends IntFunction {
    public HSBFunction() {
        super(3);
    }

    public String getName() {
        return "HSB";
    }

    public int getInt(Tuple t) {
        float h = param(0).getFloat(t);
        float s = param(1).getFloat(t);
        float b = param(2).getFloat(t);
        return ColorLib.hsb(h, s, b);
    }
}

//HSBA(h,s,b,a)
class HSBAFunction extends IntFunction {
    public HSBAFunction() {
        super(4);
    }

    public String getName() {
        return "HSBA";
    }

    public int getInt(Tuple t) {
        float h = param(0).getFloat(t);
        float s = param(1).getFloat(t);
        float b = param(2).getFloat(t);
        float a = param(3).getFloat(t);
        return ColorLib.hsba(h, s, b, a);
    }
}

//COLORINTERP(c1, c2, f)
class ColorInterpFunction extends IntFunction {
    public ColorInterpFunction() {
        super(3);
    }

    public String getName() {
        return "COLORINTERP";
    }

    public int getInt(Tuple t) {
        int c1 = param(0).getInt(t);
        int c2 = param(1).getInt(t);
        double f = param(2).getDouble(t);
        return ColorLib.interp(c1, c2, f);
    }
}
